/*
* game.cpp
* Game Engine
*/
#include <vector>
#include <allegro.h>
/*
* Including every classes needed
*/
#include "game.h"
#include "consts.h"
#include "stage.h"
#include "entity.h"
#include "enemy.h"
#include "item.h"
#include "charnoe.h"
#include "charnico.h"
#include "chartibo.h"
#include "hud.h"

/* spriteDraw */
void spriteDraw(BITMAP * dest, BITMAP * source, int x, int y) {
  if ( !dest || !source ) return;
  draw_sprite(dest, source, x, GAME_H - y - source->h );
}

/* Game framerate function */
volatile long cnt_speed = 0;
void cnt_speed_increase() { ++cnt_speed; }
END_OF_FUNCTION(cnt_speed_increase);

using std::vector;

BITMAP * entArr[ENTITY_LIBRARY_SIZE][ENTITY_ANIMATION_SIZE];

vector<Entity *>entVec;

engineStruct game(int character) {
  /* Locking the game speed */
  LOCK_VARIABLE(cnt_speed);
  LOCK_FUNCTION(cnt_speed_increase);
  install_int_ex(cnt_speed_increase, BPS_TO_TIMER(FRAMERATE));
  /*  Framerate is set up in consts.h  */

  int quit = 0;


  // Prior functions
  Entity::entLibInit(); // Initiliaze the Entity Library

  // Misc Setup
  vector<Enemy *>enemyVec;
  vector<HUD *>HUDVec;
  vector<Item *>itemVec;


  int entCheck;

  // Character setup
  Character * player;  
  switch(character) {
    default: player = new CharacterNoe(); break;
    case 1:
      //player = new CharTibo();
      
    break;
    case 2:
      player = new CharacterNico();
      player->__setX(-160);
    break;
  }
  player->__changeSequence(ANIMATION_STAND_R);
  player->__setY(GROUND_HEIGHT);

  // Screen setup
  BITMAP * buffer = create_bitmap(GAME_W, GAME_H);

  // Stage
  Stage * stage = new Stage("data/stage1.cfg");

  // Linking
  stage->__linkCharacter(player);
  player->__linkEntVec(&entVec);
  player->__linkHUD(&HUDVec);
  stage->_enemyVec = &enemyVec;
  stage->__linkItemVec(&itemVec);
  stage->__linkHUDVec(&HUDVec);


  // <dev>
  // Title Displaying
  int displayTitle = FRAMERATE * 2;
  // TMP fix
  int enemyKilled = 0;
  // Boss life tests HUD
  // </dev>
 
  // Iterator declarations (alleviate the eye pain)
  vector<Item*>::iterator itemIt;
  vector<Enemy*>::iterator enemyIt;

  while(!quit) {
    while ( cnt_speed > 0 ) {
      if ( displayTitle > 0 ) --displayTitle;
      /*
      *  Logic Engine
      *  All the calculation stuff goes in there
      */
      if ( key[KEY_ESC]) quit = 1;


      // Stage
      stage->__update();

      // Player
      if ( !stage->__isScrolling() ) player->__readKeyboard();
      player->__update();

      // Enemies
      enemyKilled = 0;
      for(enemyIt = enemyVec.begin(); enemyIt != enemyVec.end();) {
	  Enemy* enemy = *enemyIt;

        if ( !enemy->__update()) {
	  ++enemyKilled;
          delete enemy;
          enemyIt = enemyVec.erase(enemyIt);
        }
	else ++enemyIt;
      }
      if ( enemyKilled > 0 )
	  stage->__killEnemy(enemyKilled);
      // Items
      for(itemIt = itemVec.begin(); itemIt != itemVec.end();) {
	Item* item = *itemIt;
        if ( player->__collidesWith(item)) {
          player->__getItem(item->__getId(), item->__getCustomValue(1), item->__getCustomValue(2),item->__getCustomValue(3));
          delete item;
          itemIt = itemVec.erase(itemIt); 
        }
	else ++itemIt;
      }
      // Entities
      for(vector<Entity*>::iterator entVec_i = entVec.begin(); entVec_i != entVec.end();) {
        entCheck = 0;
        if ( !(*entVec_i)->__update()) {
          delete(*entVec_i);
          entVec_i = entVec.erase(entVec_i);
	  continue;
        }
        else {
          for(vector<Enemy*>::iterator enemyVec_i = enemyVec.begin(); enemyVec_i != enemyVec.end();) {
            if ( (*entVec_i)->__collidesWith(*enemyVec_i)
            && (*enemyVec_i)->__isAlive() &&
            static_cast<eShot*>(*entVec_i)->__getOwner() != TYPE_ENEMY )  {
              // Collision : bullets and enemies
              if ( (*entVec_i)->__getType() == TYPE_BULLET ) {
                (*enemyVec_i)->__hit(static_cast<eShot*>(*entVec_i)->__getDamage());
                entCheck = 1;
                delete (*entVec_i);
                entVec_i = entVec.erase(entVec_i);
              }
              else (*enemyVec_i)->__hit(static_cast<eHit*>(*entVec_i)->__getDamage());
              break;
            }
            if ( (*entVec_i)->__collidesWith(player) &&
            static_cast<eShot*>(*entVec_i)->__getOwner() == TYPE_ENEMY ) {
              entCheck = 1;
              delete (*entVec_i);
              entVec_i = entVec.erase(entVec_i);
              player->__hit();
              if ( player->__getLife() < 1 )
                quit = 1; 
              break;

            }
            ++enemyVec_i;
          }
          if (!entCheck) ++entVec_i;
        }
      }

      // Items
      for(vector<Item*>::iterator itemVec_i = itemVec.begin(); itemVec_i != itemVec.end();) {
        (*itemVec_i)->__update();
        ++itemVec_i;
      }
      cnt_speed--;
    }
    /*
    * Draw Engine
    */
    // Order of drawing : Stage, enemies, entities, items, character, HUD

    // Stage
    draw_sprite(buffer, stage->__getSprite(), stage->__getX(), 0);
    // Enemies
    for(vector<Enemy *>::iterator enemyVec_i = enemyVec.begin(); enemyVec_i != enemyVec.end(); ++enemyVec_i)
      spriteDraw(buffer, (*enemyVec_i)->__getSprite(), (*enemyVec_i)->__getX(), (*enemyVec_i)->__getY());
    // Entities
    for(vector<Entity *>::iterator entVec_i = entVec.begin(); entVec_i != entVec.end();  ++entVec_i)
      spriteDraw(buffer, (*entVec_i)->__getSprite(), (*entVec_i)->__getX(), (*entVec_i)->__getY());
    // Items
    for(vector<Item *>::iterator itemVec_i = itemVec.begin(); itemVec_i != itemVec.end();  ++itemVec_i)
      spriteDraw(buffer, (*itemVec_i)->__getSprite(), (*itemVec_i)->__getX(), (*itemVec_i)->__getY());
    // Character
    spriteDraw(buffer, player->__getSprite(), player->__getX(), player->__getY());
    // HUD
    for(vector<HUD *>::iterator HUDVec_i = HUDVec.begin(); HUDVec_i != HUDVec.end(); ++HUDVec_i)
      spriteDraw(buffer, (*HUDVec_i)->__getSprite(), (*HUDVec_i)->__getX(), (*HUDVec_i)->__getY());

    // Title
    if ( displayTitle > 0 )
      draw_sprite(buffer, stage->__getTitle(),
      GAME_W/2 - stage->__getTitle()->w/2,
      GAME_H/2 - stage->__getTitle()->h/2);
    // Double frame buffering
    blit(buffer, screen, 0, 0, 0, 0, buffer->w, buffer->h);

    clear_bitmap(buffer);
    clear_keybuf();
  } 

  delete player;
  delete stage;
  enemyVec.clear();
  entVec.clear();
  itemVec.clear();
  HUDVec.clear();
  Entity::entLibDestroy();
  destroy_bitmap(buffer);

  engineStruct comm;
  comm.mode = MODE_MENU;
  return comm;
}
